Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPCs draw weapon on dead monsters #13

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v1.1.0 (TBA)
### General
* Fix [#13](https://g1cp.org/issues/13): NPCs no longer draw their weapons on dead monsters.
* Fix [#52](https://g1cp.org/issues/52): The grindstone in the New Camp now correctly requires a sword blade to use.
* Fix [#149](https://g1cp.org/issues/149): The armor "Improved ore Armor" is now correctly labelled as "Improved Ore Armor".
* Fix [#192](https://g1cp.org/issues/192): Mages (NPCs fighting only with spells) no longer auto-equip weapons (e.g. after trading). This requires fix #59 to be active.
Expand Down
1 change: 1 addition & 0 deletions docs/changelog_de.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v1.1.0 (TBA)
### General
* Fix [#13](https://g1cp.org/issues/13): NSCs ziehen nicht mehr ihre Waffe gegenüber toten Monstern.
* Fix [#52](https://g1cp.org/issues/52): Der Schleifstein im Neuen Lager setzt zur Benutzung nun korrekt eine Schwertklinge voraus.
* Fix [#144](https://g1cp.org/issues/144): Die Rüstung "Gomez'Rüstung" heißt nun korrekt "Gomez' Rüstung".
* Fix [#145](https://g1cp.org/issues/145): Die Rüstung "leichte Söldnerrüstung" heißt nun korrekt "Leichte Söldnerrüstung".
Expand Down
63 changes: 63 additions & 0 deletions src/Ninja/G1CP/Content/Fixes/Session/fix013_NpcIgnoreDeadMonster.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* #13 NPCs draw weapon on dead monsters
*/
func int G1CP_013_NpcIgnoreDeadMonster() {
if (MEM_GetSymbolIndex("ZS_AssessMonster") != -1)
&& (MEM_GetSymbolIndex("ZS_AssessMonster_Loop") != -1)
&& (MEM_GetSymbolIndex("C_NpcIsDown") != -1) {
HookDaedalusFuncS("ZS_AssessMonster", "G1CP_013_NpcIgnoreDeadMonster_HookStart");
HookDaedalusFuncS("ZS_AssessMonster_Loop", "G1CP_013_NpcIgnoreDeadMonster_HookLoop");
return TRUE;
} else {
return FALSE;
};
};

/*
* This function intercepts the start of the NPC state to introduce more conditions
*/
func void G1CP_013_NpcIgnoreDeadMonster_HookStart() {
G1CP_ReportFuncToSpy();

// Check if other is even valid
if (!Hlp_IsValidNpc(other)) {
AI_ContinueRoutine(self);
return;
};

// Check if other is down
MEM_PushInstParam(other);
MEM_CallByString("C_NpcIsDown"); // This function exists, as established by the function above
if (MEM_PopIntResult()) {
AI_ContinueRoutine(self);
return;
};

// Otherwise continue with original function
ContinueCall();
};

/*
* This function intercepts the NPC state to introduce more conditions
*/
func int G1CP_013_NpcIgnoreDeadMonster_HookLoop() {
G1CP_ReportFuncToSpy();

// Define possibly missing symbols locally
const int LOOP_END = 1;

// Check if other is even valid
if (!Hlp_IsValidNpc(other)) {
return LOOP_END;
};

// Check if other is down
MEM_PushInstParam(other);
MEM_CallByString("C_NpcIsDown"); // This function exists, as established by the function above
if (MEM_PopIntResult()) {
return LOOP_END;
};

// Otherwise continue with original function
ContinueCall();
};
117 changes: 117 additions & 0 deletions src/Ninja/G1CP/Content/Tests/test013.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* #13 NPCs draw weapon on dead monsters
*
* A test NPC and a scavenger is inserted. The scavenger is killed off.
*
* Expected behavior: The NPC does not react to the dead monster.
*/
func void G1CP_Test_013() {
if (!G1CP_TestsuiteAllowManual) {
return;
};

// Check if AI state exists
var int symbId; symbId = MEM_GetSymbolIndex("ZS_StandAround");
if (symbId == -1) {
G1CP_TestsuiteErrorDetail("AI state 'ZS_StandAround' not found");
return;
};

// Check if monster exists
var int scavengerId; scavengerId = MEM_GetSymbolIndex("Scavenger");
if (scavengerId == -1) {
G1CP_TestsuiteErrorDetail("NPC 'Scavenger' not found");
return;
};

// Insert dead monster
var string wp; wp = Npc_GetNextWP(hero);
Wld_InsertNpc(scavengerId, wp);
var C_Npc monster; monster = Hlp_GetNpc(scavengerId);
if (!Hlp_IsValidNpc(monster)) {
G1CP_TestsuiteErrorDetail("Failed to insert monster NPC");
return;
};

// Insert test NPC
wp = Npc_GetNearestWP(hero);
Wld_InsertNpc(G1CP_Test_013_Npc, wp);
var C_Npc test; test = Hlp_GetNpc(G1CP_Test_013_Npc);
if (!Hlp_IsValidNpc(test)) {
G1CP_TestsuiteErrorDetail("Failed to insert NPC");
return;
};
};

/*
* Testing NPC to see reaction
*/
instance G1CP_Test_013_Npc(C_Npc) {
name = "Test 13";
attribute[0] = 2;
attribute[1] = 2;
senses = 7;
senses_range = 2000;
start_aistate = ZS_G1CP_Test_013_NpcRountine;
Mdl_SetVisual(self, "HUMANS.MDS");
Mdl_SetVisualBody(self, "HUM_BODY_NAKED0", 1, 1, "Hum_Head_Fighter", 1, 1, -1);
};

/*
* AI state is stared once the NPC is properly inserted
*/
func void ZS_G1CP_Test_013_NpcRountine() {};
func int ZS_G1CP_Test_013_NpcRountine_Loop() {

// Define possibly missing symbols locally
const int ATR_HITPOINTS = 0;

// Trigger the reaction
if (Npc_GetStateTime(self) > 1) {
// Only kill off the monster now, cannot spawn an already dead monster
var C_Npc monster; monster = Hlp_GetNpc(MEM_GetSymbolIndex("Scavenger"));
monster.attribute[ATR_HITPOINTS] = 0;

// AI_StartState(self, symbId, 0, ""); // Does not work, expects func parameter
MEM_PushInstParam(self);
MEM_PushIntParam(MEM_GetSymbolIndex("ZS_StandAround")); // Func parameter as integer
MEM_PushIntParam(0);
MEM_PushStringParam("");
MEM_Call(AI_StartState);
return 1;
};
return 0;
};
func void ZS_G1CP_Test_013_NpcRountine_End() {};

/*
* Add dialog to remove the NPC
*/
instance G1CP_Test_013_Dialog(C_Info) {
npc = G1CP_Test_013_Npc;
condition = G1CP_Test_013_Dialog_Condition;
information = G1CP_Test_013_Dialog_Info;
important = 1;
permanent = 1;
};
func int G1CP_Test_013_Dialog_Condition() {
var int symbId; symbId = MEM_GetSymbolIndex("ZS_Talk");
if (!symbId) {
return FALSE;
};
// Npc_IsInState(self, symbId)
MEM_PushInstParam(self);
MEM_PushIntParam(symbId);
MEM_Call(Npc_IsInState);
if (MEM_PopIntResult()) {
return TRUE;
};
};
func void G1CP_Test_013_Dialog_Info() {
AI_StopProcessInfos(self);
AI_StopProcessInfos(other);

// Delete the NPC once finished
MEM_WriteInt(_@(self.bodymass)+8, 0); // Clear start_aistate
AI_Function_I(hero, Wld_RemoveNpc, G1CP_Test_013_Npc);
};
1 change: 1 addition & 0 deletions src/Ninja/G1CP/Content/patchInit.d
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func void Ninja_G1CP_Menu(var int menuPtr) {
G1CP_010_FollowWalkMode(); // #10
G1CP_011_PassGuardsCombatMode(); // #11
G1CP_012_RangedDoubleXP(); // #12
G1CP_013_NpcIgnoreDeadMonster(); // #13
G1CP_015_HoratioStrength(); // #15
G1CP_016_ThorusBribeDialog(); // #16
G1CP_017_JackalProtectionMoney(); // #17
Expand Down
2 changes: 2 additions & 0 deletions src/Ninja/G1CP/Content_G1.src
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Content\Fixes\Session\fix009_NpcStateFlee.d
Content\Fixes\Session\fix010_FollowWalkMode.d
Content\Fixes\Session\fix011_PassGuardsCombatMode.d
Content\Fixes\Session\fix012_RangedDoubleXP.d
Content\Fixes\Session\fix013_NpcIgnoreDeadMonster.d
Content\Fixes\Session\fix015_HoratioStrength.d
Content\Fixes\Session\fix016_ThorusBribeDialog.d
Content\Fixes\Session\fix017_JackalProtectionMoney.d
Expand Down Expand Up @@ -114,6 +115,7 @@ Content\Tests\test009.d
Content\Tests\test010.d
Content\Tests\test011.d
Content\Tests\test012.d
Content\Tests\test013.d
Content\Tests\test015.d
Content\Tests\test016.d
Content\Tests\test017.d
Expand Down