blob: 58b7d6ac6cf145779061e7a4824c003084a22c13 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// ranged_weapon.c -- Standardobjekt fuer Fernkampfwaffen
4//
5// $Id: ranged_weapon.c 8820 2014-05-14 19:31:46Z Zesstra $
6
7// Gebraucht wird Munition mit einer MUN_* id. Dieses Objekt sollte ausserdem
8// P_DAM_TYPE setzen sowie P_SHOOTING_WC und ggf. P_HIT_FUNC. In der Regel
9// sollten diese Objekte units sein, da groessere Anzahlen davon gebraucht
10// werden.
11#pragma strong_types
12#pragma save_types
13#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020014#pragma range_check
15
16inherit "/std/living/skill_utils";
17inherit "/std/weapon";
18
19#include <ranged_weapon.h>
20
21#include <properties.h>
22#include <defines.h>
23#include <combat.h>
24#include <new_skills.h>
25#include <unit.h>
26
27static int last_shoot;
28
29protected void create()
30{
31 ::create();
32
33 SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
34 SetProp(P_STRETCH_TIME, 1);
35 SetProp(P_AMMUNITION, MUN_ARROW);
36 SetProp(P_SHOOTING_WC, 70);
37 SetProp(P_RANGE, 50);
38 SetProp(P_INFO, "Syntax: schiesse WAS auf WEN?\n");
39 // fuer den Fall, das man den Bogen als Knueppel nutzt...
40 SetProp(P_WC, 30);
41 SetProp(P_QUALITY, 100);
42
43 AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
44}
45
46static int shoot_dam(mapping shoot)
47// ausgegliedert, damit man diese Funktion ggf. ueberschreiben kann.
48{ mixed res;
49 object hitfunc;
50 closure usk;
51
52 // diesselbe Formel wie bei Nahkampfwaffen, nur das hier DEX anstatt STR
53 // benutzt wird, und die WC der Munition noch addiert wird
54 shoot[SI_SKILLDAMAGE] = random(1+( 2*(shoot[P_WC]+shoot[P_SHOOTING_WC])
55 + 10*PL->QueryAttribute(A_DEX) )/3);
56 // HitFunc der Munition addieren
57 if ( objectp(hitfunc=(shoot[P_AMMUNITION]->QueryProp(P_HIT_FUNC))) )
58 shoot[SI_SKILLDAMAGE] += (hitfunc->HitFunc(shoot[SI_ENEMY]));
59
60 usk=symbol_function("UseSkill",PL);
61
62 // Schuss-Skill der Gilde fuer diesen Munitionstyp anwenden
63 if ( mappingp(res=funcall(usk,SHOOT(shoot[P_WEAPON_TYPE]),
64 deep_copy(shoot))) )
65 SkillResTransfer(res, &shoot);
66
67 // Allgemeinen Schuss-Skill der Gilde anwenden
68 if ( mappingp(res=funcall(usk,SK_SHOOT,deep_copy(shoot))) )
69 SkillResTransfer(res, &shoot);
70
71 return shoot[SI_SKILLDAMAGE];
72}
73
74static string FindRangedTarget(string str, mapping shoot)
75{ string wen;
76 mixed area;
77 int psr;
78
79 if ( sscanf(str, "%s auf %s", str, wen)!=2 )
80 {
81 shoot[SI_ENEMY]=PL->SelectFarEnemy(PL->PresentEnemies(),1,4);
82 str = shoot[P_WEAPON_TYPE];
83 }
84 else if ( objectp(shoot[SI_ENEMY]=present(wen, environment(PL))) )
85 {
86 return str;
87 }
88 else if ( ( (area=(environment(PL)->QueryProp(P_TARGET_AREA)))
89 || (area=environment(environment(PL))) )
90 && ( objectp(area)
91 || ( stringp(area)
92 && objectp(area=find_object(area)) ) )
93 && (psr=environment(PL)->QueryProp(P_SHOOTING_AREA)) )
94 {
95 shoot[SI_ENEMY]=present(wen, area);
96 if ( shoot[SI_ENEMY] && (psr>QueryProp(P_RANGE)) )
97 {
98 write(break_string((shoot[SI_ENEMY]->Name(WER))+
99 " ist leider ausserhalb der Reichweite "+name(WESSEN, 1)+".",
100 78));
101 return 0;
102 }
103 }
104
105 if ( !shoot[SI_ENEMY] )
106 {
107 write("Es ist kein Feind vorhanden auf den Du schiessen koenntest.\n");
108 return 0;
109 }
110
111 return str;
112}
113
114static int cmd_shoot(string str)
Zesstrad954ba02020-05-27 00:28:38 +0200115{
MG Mud User88f12472016-06-24 23:31:02 +0200116 mapping shoot;
117
118 if ( PL->QueryProp(P_GHOST) )
119 {
120 write("Deine immateriellen Finger gleiten durch die Waffe hindurch.\n");
121 return 1;
122 }
Zesstrad954ba02020-05-27 00:28:38 +0200123
MG Mud User88f12472016-06-24 23:31:02 +0200124 if ( !QueryProp(P_NOGET) )
125 {
126 // Katapulte oder aehnliches die von Magiern aufgestellt werden
127 // muessen natuerlich nicht gezueckt werden :).
128 if ( !QueryProp(P_WIELDED) )
129 {
130 notify_fail(break_string("Du solltest "+name(WEN, 1)+
131 " dazu schon zuecken.", 78));
132 return 0;
133 }
134
135 if ( (PL->InFight()) && ((PL->PresentPosition())<2) )
136 {
137 write(break_string(
138 "Du solltest Dich erst etwas weiter zurueckziehen, um mit "+
139 name(WEM, 1)+" schiessen zu koennen!", 78));
140 return 1;
141 }
142 }
143
144 if ( ( (QueryProp(P_EQUIP_TIME)+QueryProp(P_STRETCH_TIME)*2) > time() )
145 || ( (last_shoot+QueryProp(P_STRETCH_TIME)) > absolute_hb_count() ) )
146 {
147 write(break_string("Du kannst mit "+name(WEM, 1)+
148 " noch nicht wieder schiessen.", 78));
149 return 1;
150 }
151
152 if ( (PL->QueryProp(P_DISABLE_ATTACK))>0 )
153 {
154 write(break_string("Solange Du noch gelaehmt bist, kannst Du "+
155 name(WEN, 1)+" nicht benutzen!", 78));
156 return 1;
157 }
158
159 if ( PL->QueryProp(P_ATTACK_BUSY) )
160 {
161 write("Nicht so hektisch! So schnell bist Du nicht.\n");
162 return 1;
163 }
164
165 // P_ATTACK_BUSY natuerlich auch setzen...
166 PL->SetProp(P_ATTACK_BUSY,1);
167
168 // Info-Mapping erzeugen
169 shoot = ([ P_WEAPON : ME,
170 P_WEAPON_TYPE : QueryProp(P_AMMUNITION),
171 P_STRETCH_TIME : QueryProp(P_STRETCH_TIME),
172 P_WC : QueryProp(P_SHOOTING_WC),
173 SI_SPELL : 0
174 ]);
175
176 if ( !stringp(str) || str=="" )
177 str=shoot[P_WEAPON_TYPE];
178 else if (str[0..3]=="mit ")
179 str=str[4..];
180 else if (str[0..3]=="auf ")
181 str=shoot[P_WEAPON_TYPE]+" "+str;
182
183 if ( !(str=FindRangedTarget(str,shoot)) )
184 return 1;
185
Zesstrad954ba02020-05-27 00:28:38 +0200186 int|string no_at = shoot[SI_ENEMY]->QueryProp(P_NO_ATTACK);
187 if (no_at)
MG Mud User88f12472016-06-24 23:31:02 +0200188 {
189 if ( stringp(no_at) )
190 write(no_at);
191 else
192 write(break_string("Du kannst "+(shoot[SI_ENEMY]->name(WEN, 1))+
193 " nicht angreifen.", 78));
194 return 1;
195 }
Zesstrad954ba02020-05-27 00:28:38 +0200196 else if ( shoot[SI_ENEMY]->QueryProp(P_GHOST) )
197 {
198 write(break_string("Aber "+(shoot[SI_ENEMY]->name(WER, 1))+
199 " ist doch ein Geist!", 78));
200 return 1;
201 }
MG Mud User88f12472016-06-24 23:31:02 +0200202 else if ( shoot[SI_ENEMY]==PL )
203 {
204 write("Du kannst doch nicht auf Dich selbst schiessen!\n");
205 return 1;
206 }
Zesstrad954ba02020-05-27 00:28:38 +0200207 object quiver;
MG Mud User88f12472016-06-24 23:31:02 +0200208 if ( !(shoot[P_AMMUNITION]=present(str, PL))
209 && ( !objectp(quiver=PL->QueryArmourByType(AT_QUIVER))
210 || !(shoot[P_AMMUNITION]=present(str, quiver)) ) )
211 {
212 if ( str==shoot[P_WEAPON_TYPE] )
213 write(break_string("Du hast keine passende Munition bei Dir, um "+
214 "mit "+name(WEM, 1)+" schiessen zu koennen.",
215 78));
216 else
217 write(break_string("Du hast kein '"+str+"' bei Dir.", 78));
218 return 1;
219 }
220
221 if ( !shoot[P_AMMUNITION]->id(shoot[P_WEAPON_TYPE]) )
222 {
223 write(break_string((shoot[P_AMMUNITION]->Name(WER))+" kannst Du mit "+
224 name(WEM, 1)+" nicht verschiessen.", 78));
225 return 1;
226 }
227
228 shoot[P_AMMUNITION]->SetProp(U_REQ, 1);
229
230 shoot[SI_SKILLDAMAGE_MSG] = shoot[SI_SKILLDAMAGE_MSG2]
231 = shoot[P_AMMUNITION]->name(WEN);
232 shoot[SI_SKILLDAMAGE_TYPE] = ( shoot[P_AMMUNITION]->QueryProp(P_DAM_TYPE)
233 || ({DT_PIERCE}) );
234
235 shoot[P_SHOOTING_WC] = shoot[P_AMMUNITION]->QueryProp(P_SHOOTING_WC);
236
Zesstrad954ba02020-05-27 00:28:38 +0200237 int dam=shoot_dam(shoot);
MG Mud User88f12472016-06-24 23:31:02 +0200238
239 // Textausgabe
240 if (shoot[SI_ENEMY])
241 {
242 tell_object(PL,break_string(
243 "Du schiesst "+shoot[SI_SKILLDAMAGE_MSG]+" auf "+
244 shoot[SI_ENEMY]->name(WEN, 1)+".", 78) );
245 tell_room(environment(PL), break_string(
246 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
247 shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
248 ({ shoot[SI_ENEMY], PL }) );
249 if ( environment(PL)!=environment(shoot[SI_ENEMY]) )
250 tell_room(environment(shoot[SI_ENEMY]),break_string(
251 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
252 shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
253 ({ shoot[SI_ENEMY] }) );
254 tell_object(shoot[SI_ENEMY], break_string(
255 PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+
256 " auf Dich ab.",78) );
257
258 shoot[SI_ENEMY]->Defend(dam,shoot[SI_SKILLDAMAGE_TYPE],shoot[SI_SPELL],PL);
259 }
260 // Munition verbrauchen
261 if ( shoot[P_AMMUNITION]->IsUnit() )
262 shoot[P_AMMUNITION]->AddAmount(-1);
263 else
264 shoot[P_AMMUNITION]->remove();
265
266 // es wird an dieser stelle absolute_hb_count anstelle von time() benutzt
267 // um dem Spieler wirklich die vollen 2 sek Zeit zu geben um zu reagieren
268 // ohne das er dadurch einen Schaden hat (wie er es z.b. bei time() haette)
269 last_shoot=absolute_hb_count();
270
271 return 1;
272}
273